package bancosys.tec.services.implementor.descriptor;

import java.util.List;

import jmine.tec.di.metadata.DependencyMetaData;
import jmine.tec.di.scope.TypedScope;
import jmine.tec.di.type.TypeConvertionException;

/**
 * InstanceCreator lazy que guarda a instancia criada.
 * 
 * @author takeshi
 */
public class LazySingletonInstanceCreator implements InstanceCreator {

    /**
     * Classe que guarda um futuro
     * 
     * @author takeshi
     */
    private static interface Accessor {
        /**
         * @param scope o {@link TypedScope}
         * @return {@link Object}
         * @throws TypeConvertionException e
         */
        Object get(TypedScope scope) throws TypeConvertionException;
    }

    private final InstanceCreator delegate;

    /**
     * Guarda a instancia criada
     */
    private static final class SingletonAccessor implements Accessor {

        private final Object target;

        /**
         * @param target o alvo a ser devolvido
         */
        public SingletonAccessor(Object target) {
            super();
            this.target = target;
        }

        /**
         * {@inheritDoc}
         */
        public Object get(TypedScope scope) {
            return this.target;
        }

    }

    /**
     * Accessor que lanca a excecao original
     * 
     * @author takeshi
     */
    private static final class ExceptionThrowerAccessor implements Accessor {
        private final TypeConvertionException exception;

        /**
         * @param exception {@link TypeConvertionException}
         */
        public ExceptionThrowerAccessor(TypeConvertionException exception) {
            super();
            this.exception = exception;
        }

        /**
         * {@inheritDoc}
         */
        public Object get(TypedScope scope) throws TypeConvertionException {
            throw this.exception;
        }

    }

    private Accessor accessor = new Accessor() {
        public Object get(TypedScope scope) throws TypeConvertionException {
            synchronized (LazySingletonInstanceCreator.this) {
                if (LazySingletonInstanceCreator.this.accessor == this) {
                    try {
                        Object target = LazySingletonInstanceCreator.this.delegate.hydrateAndCreateInstance(scope);
                        LazySingletonInstanceCreator.this.accessor = new SingletonAccessor(target);
                    } catch (TypeConvertionException e) {
                        LazySingletonInstanceCreator.this.accessor = new ExceptionThrowerAccessor(e);
                    }
                }
                return LazySingletonInstanceCreator.this.accessor.get(scope);
            }
        }
    };

    /**
     * @param delegate the delegate
     */
    public LazySingletonInstanceCreator(InstanceCreator delegate) {
        super();
        this.delegate = delegate;
    }

    /**
     * {@inheritDoc}
     */
    public Object hydrateAndCreateInstance(TypedScope properties) throws TypeConvertionException {
        return this.accessor.get(properties);
    }

    /**
     * {@inheritDoc}
     */
    public List<String> validateRequiredProperties(TypedScope properties) {
        return this.delegate.validateRequiredProperties(properties);
    }

    /**
     * {@inheritDoc}
     */
    public List<DependencyMetaData> getDependencies() {
        return this.delegate.getDependencies();
    }

}
